#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <alloca.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <rpm/rpmlib.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>

#include "cached_hash.h"

#include <apt-pkg/error.h>
#include <apt-pkg/tagfile.h>
#include <apt-pkg/configuration.h>

CachedHash::CachedHash(pgpHashAlgo HType, string DirName, string Domain, string Suffix)
{
   HashType = HType;
   if (DirName.empty())
       return;
   string fname = DirName;
   for (string::iterator i = fname.begin(); i != fname.end(); ++i)
      if ('/' == *i)
	 *i = '_';
   CacheFileName = _config->FindDir("Dir::Cache", "/var/cache/apt") + '/' +
		   Domain + '/' + fname + Suffix;

   FILE *f = fopen(CacheFileName.c_str(), "r");
   if (!f)
      return;

   while (1)
   {
      char buf[BUFSIZ];
      if (!fgets(buf, sizeof(buf), f))
	 break;
      char *p1 = strchr(buf, ' ');
      assert(p1);

      string File;
      File = string(buf, p1++);
      char *p2 = strchr(p1, ' ');
      assert(p2);

      FileData Data;
      Data.Hash = string(p1, p2++);
      Data.TimeStamp = atol(p2);
      HashTable[File] = Data;
   }

   fclose(f);
}


CachedHash::~CachedHash()
{
   if (CacheFileName.empty())
       return;
   FILE *f = fopen(CacheFileName.c_str(), "w+");
   if (f)
   {
      for (map<string,FileData>::const_iterator I = HashTable.begin();
	   I != HashTable.end(); I++ )
      {
         const string &File = (*I).first;
         const FileData &Data = (*I).second;
         fprintf(f, "%s %s %lu\n",
	         File.c_str(), Data.Hash.c_str(), Data.TimeStamp );
      }
      fclose(f);
   }
}

bool CachedHash::HashForFile(string FileName, time_t TimeStamp, char *outbuf)
{
   if (HashTable.find(FileName) != HashTable.end()
       && TimeStamp == HashTable[FileName].TimeStamp )
   {
      strcpy(outbuf, HashTable[FileName].Hash.c_str());
   }
   else
   {
      int Res = 0;
      unsigned char Buf[BUFSIZ];
      unsigned long Size = 0;

      DIGEST_CTX HashCtx = rpmDigestInit(HashType, RPMDIGEST_NONE);
      FileFd File(FileName, FileFd::ReadOnly);
      Size = File.Size();

      while (Size != 0) {
	  Res = read(File.Fd(), Buf, std::min(Size, (unsigned long)sizeof(Buf)));
	  if (Res < 0 || (unsigned) Res != std::min(Size,(unsigned long)sizeof(Buf)))
	      return false;
	  Size -= Res;
	  if (rpmDigestUpdate(HashCtx, Buf, Res))
	      return false;
      }
      File.Close();
      void *data = NULL;
      size_t dlen = 0;

      rpmDigestFinal(HashCtx, &data, &dlen, 1);
      FileData Data;
      Data.Hash = string((const char*)data);
      Data.TimeStamp = TimeStamp;
      HashTable[FileName] = Data;
      strcpy(outbuf, (const char*)data);
      free(data);
      HashCtx = NULL;
   }

   return true;
}

// vim:sts=3:sw=3
